package com.zshan.clinic.cms.aop;


import com.zshan.clinic.cms.common.core.domain.entity.SysUser;
import com.zshan.clinic.cms.common.utils.SecurityUtils;
import com.zshan.clinic.common.constant.CommonConstant;
import com.zshan.clinic.common.util.json.JsonUtil;
import com.zshan.clinic.common.util.string.StringUtil;
import com.zshan.clinic.common.util.uuid.IdBuilder;
import lombok.extern.apachecommons.CommonsLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 统一日志切面
 */
@Aspect
@Service
@Order(200)
@CommonsLog
public class LogAspect {

    private static Map<String, String[]> parameterNameCaches = new ConcurrentHashMap<String, String[]>();

    private static LocalVariableTableParameterNameDiscoverer parameterNameDiscovere = new LocalVariableTableParameterNameDiscoverer();

    @Pointcut("execution(public * com.zshan.clinic.*.service..*.*(..))")
    private void service() {

    }

    @Pointcut("execution(public * com.zshan.clinic.*.manager..*.*(..))")
    private void manager() {

    }

    @Pointcut("execution(public * com.zshan.clinic.*.controller..*.*(..))")
    private void controller() {

    }

    @Pointcut("service() || manager() || controller()")
    public void allImportantMethods() {}

    @Around("allImportantMethods()")
    public Object setMDC(ProceedingJoinPoint point) throws Throwable {
        try {
            if (StringUtil.isEmpty(MDC.get("requestId"))) {
                MDC.put("requestId", IdBuilder.getID());
                MDC.put("startTime", String.valueOf(System.currentTimeMillis()));
                MDC.put("methodName", StringUtil.append(point.getSignature().getDeclaringTypeName(), ".", point.getSignature().getName()));
            }
            Method method = getMethod(point);
            printParam(point);
            Object result = point.proceed();
            printResult(point, result);
            return result;
        } finally {
            if (StringUtil.append(point.getSignature().getDeclaringTypeName(), ".", point.getSignature().getName()).equals(MDC.get("methodName"))) {
                long startTime = Long.parseLong(MDC.get("startTime"));
                String errorMsg = "provider." + MDC.get("methodName") + "接口耗时" + (System.currentTimeMillis() - startTime) + "ms";
                log.info(errorMsg);
                MDC.remove("requestId");
                MDC.remove("startTime");
                MDC.remove("methodName");
            }
        }
    }

    public String getMethodName(ProceedingJoinPoint point) {
        return point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName();
    }


    /**
     * 打印结果
     *
     * @param point
     * @param result
     */
    public void printResult(ProceedingJoinPoint point, Object result) {
        Class<?> returnType = ((MethodSignature) point.getSignature()).getReturnType();
        if ("void".equals(returnType.getName())) {
            return;
        }
        String jsonStr = JsonUtil.obj2Json(result);
        if(StringUtil.isNotBlank(jsonStr)){
            if (jsonStr.length() > CommonConstant.DEFAULT_1000) {
                jsonStr = jsonStr.substring(CommonConstant.DEFAULT_0, CommonConstant.DEFAULT_1000);
            }
        }
        log.info("返回结果:" + jsonStr);
    }

    /*打印输入参数*/
    public void printParam(ProceedingJoinPoint point) {
        try {
            SysUser sysUser = SecurityUtils.getSysUser();
            final Map param = getParam(point);
            Map<String, Object> params = param;
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("目标方法:"+ StringUtil.append(point.getSignature().getDeclaringTypeName()+"."+point.getSignature().getName()));
            stringBuffer.append("    输入参数:"+ JsonUtil.obj2Json(params));
            if(sysUser != null){
                stringBuffer.append("    操作人信息【用户ID:"+sysUser.getUserId()+"，userName:"+sysUser.getUserName()+"】");
            }
            log.info(stringBuffer.toString());
        } catch (Exception e) {
            log.error("获取参数错误,错误信息:", e);
        }
    }

    /**
     * 获取客户端参数
     *
     * @param point
     * @return
     * @throws NoSuchMethodException
     */
    private Map getParam(ProceedingJoinPoint point) throws NoSuchMethodException {
        String methodLongName = point.getSignature().toLongString();
        String[] parameterNames = parameterNameCaches.get(methodLongName);
        if (parameterNames == null) {
            Method method = getMethod(point);
            parameterNames = parameterNameDiscovere.getParameterNames(method);
            parameterNameCaches.put(methodLongName, parameterNames);
        }
        Object[] args = point.getArgs();
        Map<String, Object> params = new HashMap<>();
        if (args.length == parameterNames.length) {
            for (int i = 0, len = args.length; i < len; i++) {
                params.put(parameterNames[i], args[i]);
            }
        }
        return params;
    }

    /**
     * 获取当前执行的方法
     *
     * @param point
     * @return
     */
    private Method getMethod(ProceedingJoinPoint point) {
        return ((MethodSignature) point.getSignature()).getMethod();
    }

}
